# coding: utf8
"""
重复多次执行前一个组件，需在PIP管理中额外安装astor库
"""

import ast
import inspect
import time

import pandas as pd

try:
    import astor
except:
    raise Exception("需在PIP管理中额外安装astor库，"
                    "菜单栏-工具-PIP管理-左下角+号-输入astor-安装按钮")


def elementExistsChecking(exists=None, timeout_min=1, try_times=3, interval=1):
    """重复多次执行前一个【元素是否存在】组件，超时和重试次数谁先超过就返回

    将此函数放入RPA设计器的【全局函数】中，然后在画布中调用此【全局函数】，
    注意：必须要放在【元素是否存在】RPA组件后使用

    exists: 【元素是否存在】组件返回值，不填则认为元素存在

    timeout_min: 超时分钟数，整数或小数，默认1分钟，为0则不限制时长
    try_times: 尝试次数，整数，默认3次，为0则不限制次数
        timeout_min 和 try_times只能有1个设为0，避免死循环

    interval: 间隔秒数，默认间隔1秒重试一次，小于0则按1秒处理，最大60s
    """
    if exists is None or not isinstance(exists, bool):
        # 未传入【元素是否存在】组件返回值，直接返回不存在
        print("未传入【元素是否存在】组件返回值到exists参数")
        return False

    if isinstance(exists, bool) and exists is True:
        # 元素存在直接返回
        return True

    # 入参校验
    if not isinstance(timeout_min, (int, float)):
        timeout_min = 1
    elif timeout_min < 0:
        timeout_min = 1
    elif timeout_min > 60:
        timeout_min = 60
    timeout = timeout_min * 60

    if not isinstance(try_times, (int, float)):
        try_times = 1
    elif try_times < 0:
        try_times = 3
    elif try_times > 100:
        try_times = 100

    if not isinstance(interval, (int, float)):
        interval = 1
    elif interval < 0:
        interval = 1
    elif interval > 100:
        interval = 60

    # 超时分钟和重试次数不能同时为0
    if timeout_min == try_times == 0:
        print("超时分钟和重试次数不能同时为0，避免进入死循环")
        return False

    # # 该全局函数被调用在哪里，获取调用者所在文件和行
    cur_frame = inspect.currentframe()
    call_frame = inspect.getouterframes(cur_frame, 2)
    callme_file, lineno_in_file = call_frame[1][1], call_frame[1][2]
    print(f"{callme_file}, {lineno_in_file}")

    # # 读取调用者文件内容到df中，便于后续查找上一个组件
    with open(callme_file, "r", encoding="utf8") as fp:
        df = pd.DataFrame(fp.readlines(), columns=["line"])

    # # 从当前行开始向上找，使用ast解析是否ubpa组件，是则拿出哪行代码，用作后续调用
    ubpa_func_code = ""
    prev_lineno = lineno_in_file - 1
    for lineno in range(prev_lineno, 0, -1):
        index = lineno - 1
        line_code = df.loc[index]["line"].strip()
        try:
            ast_node = ast.parse(line_code)
            if isinstance(ast_node.body[0].value, ast.Call):
                func_pkg_name = ast_node.body[0].value.func.value.id
                _df = df[df["line"].str.contains(func_pkg_name)
                         & df["line"].str.contains("import")
                         & df["line"].str.contains("ubpa")]
                if _df.empty:
                    continue

                func_name = ast_node.body[0].value.func.attr
                if "existed" not in func_name:
                    print(f"{func_pkg_name}.{func_name}可能不是【元素是否存在】组件")
                    continue

                # 导入组件所在模块，截取变量后面的函数代码，如：lv_1 = func()，取出func()
                exec(str(_df.iloc[0]["line"]).strip())
                ubpa_func_code = str(line_code.split("=", 1)[1]).strip()
                break
        except Exception as e:
            continue

    # 没找到【元素是否存在】组件，直接返回
    if not ubpa_func_code:
        print("没找到前一个连接【元素是否存在】组件")
        return False

    # 多次重试【元素是否存在】组件，直到超过给定时间和次数范围
    st = time.time()
    times = 1
    while True:
        # 调用前一个【元素是否存在】组件
        try:
            if eval(ubpa_func_code) is True:
                return True
        except Exception as e:
            print(f"重试前一个【元素是否存在】组件失败 {e}")

        rt = time.time() - st
        print(f"第[{times}]次，重试前一个【元素是否存在】组件，用时[{rt}]s")
        if rt >= timeout > 0:
            break
        if times >= try_times > 0:
            break
        times += 1
        time.sleep(interval)
    return False
